/*
 *    timer0_interrup.c  -  interrupt api & driver
 *
 *	  Board:		akae2440
 *    environment:	bootloader & ADS
 *    Author:		XingWenpeng
 *    Date:			2009-9-12
 *    web:			www.akaedu.org
 */
#include "led.h"
 /* Timer register */
#define TCFG0		(*((volatile unsigned int *)0x51000000))
#define TCFG1		(*((volatile unsigned int *)0x51000004))
#define TCON		(*((volatile unsigned int *)0x51000008))
#define TCNTB0		(*((volatile unsigned int *)0x5100000C))
#define TCMPB0		(*((volatile unsigned int *)0x51000010))
#define TCNTO0		(*((volatile unsigned int *)0x51000014))
/* Intrrupt register */
#define SRCPND		(*((volatile unsigned int *)0x4a000000))
#define INTMOD		(*((volatile unsigned int *)0x4a000004))
#define INTMSK		(*((volatile unsigned int *)0x4a000008))
#define PRIORITY	(*((volatile unsigned int *)0x4a00000c))
#define INTPND		(*((volatile unsigned int *)0x4a000010))
#define INTOFFSET	(*((volatile unsigned int *)0x4a000014))
#define SUBSRCPND	(*((volatile unsigned int *)0x4a000018))
#define SUBINTMSK	(*((volatile unsigned int *)0x4a00001c))

#include "task.h"

struct task_struct task[TASK_N];
struct task_struct *current  = NULL;



extern int user_sp_set(int process_sp);

extern int save_process(struct task_struct *taskpcb);
extern int schedule(void);

volatile unsigned int flag = 0;

/* delay for about some second */
static void delay(int time)	
{
	int i, j;

	for(i = 0; i < time; i++)
		for(j = 0; j < 3000000; j++)
			;
}


int os_createprocess1(void)
{
	save_process(&task[0]);												/*save TASK[0] SP, CPSR, R14, R0-R12,PC */
	
	SRCPND = SRCPND;															/*clear intrrupt*/
	INTPND = INTPND;
	
	user_sp_set(0x32800000);											/*set TASK[1] USER_sp*/
	current = &task[1];
	task[1].pid = 1;
	
	while(1)
	{
		led_on(5);
		delay(1);
		led_off(5);
		delay(1);
	}
}

int os_createprocess2(void)
{
	save_process(&task[1]);
	
	SRCPND = SRCPND;
	INTPND = INTPND;
	
	user_sp_set(0x32900000);
	current = &task[2];
	task[2].pid = 2;
	
	while(1)
	{
		led_on(6);
		delay(1);
		led_off(6);
		delay(1);
	}
}

void do_irq( void )
{ 
	switch(flag)
	{
	case 0:
				flag = 1;
				os_createprocess1();
				break;
	case 1:
				flag = 2;
				os_createprocess2();
				break;
	case 2:
				schedule();
				break;	
	}
	
	led_on(7);
	delay(4);
	led_off(7);
	SRCPND = SRCPND;
	INTPND = INTPND;	
}

void install_irq_handler( void (*isr)(void) )
{
	/* ARM irq exception vector addr is 0x00000018  */
	unsigned int * irq_vec_addr = ( unsigned int * ) 0x18;
	/* this is isr entry address, could be another address like 0x3c, 0x58... */
	unsigned int * isr_entry_addr = ( unsigned int * ) 0x38;

	unsigned int instruction;
	
	/* set the ISR entry at 0x38  */
	*isr_entry_addr = (unsigned int)isr;
	
	/* make an instruction: it is machine-code for "ldr  pc, [pc, #(38-18-8)]"  */
	instruction = ((unsigned int) isr_entry_addr  - (unsigned int)irq_vec_addr - 0x08) | 0xe59ff000;
	
	/* set this instruction at 0x18  */
	*irq_vec_addr = instruction;	
	
	return;
}

void interrupt_init(void)
{
	/* Level_1: Interrupt Source */
	
	/* timer0 : set timer0 for interrupt */
	TCFG0 = 0XFF;
	TCFG1 = 1;
	//TCNTB0 = 65535;				/*abort 1.34 second*/
	TCNTB0 = 488;					/*abort 10 millisecond*/
	TCON = 1<<1;
	TCON = (1<<0) | (1<<3);
	
/* Level_2: Interrupt Controller */
	
	/* Set Timer0 irq mode */
	INTMOD &= ~(1<<10);
	/* Unmask the Timer0 interrupt */
	INTMSK &= ~(1<<10);   
	
	/* Clear existing pending bit */
	SUBSRCPND = SUBSRCPND;
	SRCPND = SRCPND;
	INTPND = INTPND;	
	
/* Level_3: Clear ARM Core CPSR I-bit */
	__asm
	{
		MOV	r0,#0x53
		MSR	CPSR_cxsf, r0			
	}

}
extern void irq_init(void);

extern void irq_handler(void);

int main(void)
{
	int i = 0;
	
	led_init();
	install_irq_handler( irq_handler );					/*set a instruction (0xE59FF000|XXX) at 0x18*/
	
	irq_init();																	/*set IRQ_sp*/
	interrupt_init();														/*start intrrupt service*/
	user_sp_set(0x32700000);										/*set USER_sp for TASK[0]*/
	
	
	while(++i)																	/*TASK[0]*/
	{
		led_on(i%5);
		delay(1);
		led_off(i%5);
		delay(1);
	}

	return 0;
}

void __rt_entry(void)
{
	main();	
}
 
